package cn.wch.ch934xlib.assist;

import android.hardware.usb.UsbDevice;

import androidx.annotation.NonNull;

import java.util.HashMap;
import java.util.Locale;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import cn.wch.ch934xlib.exception.UartLibException;
import cn.wch.ch934xlib.gpio.GPIO_DIR;
import cn.wch.ch934xlib.gpio.GPIO_VALUE;
import cn.wch.ch934xlib.utils.LogUtil;

/**
 * storage information about GPIO
 */
public class GPIOAssist {
    private static volatile GPIOAssist gpioAssist;

    private HashMap<UsbDevice, HashMap<String, GPIOStatus>> gpioMap;

    private Lock gpioLock;
    private volatile Short gpiovalin;

    public static GPIOAssist getInstance() {
        if(gpioAssist==null){
            synchronized (GPIOAssist.class){
                gpioAssist=new GPIOAssist();
            }
        }
        return gpioAssist;
    }

    public GPIOAssist() {
        gpioMap=new HashMap<>();
        gpioLock=new ReentrantLock(true);
    }

    public void init(UsbDevice usbDevice,int serialCount,int GPIOCount){
        if(!gpioMap.containsKey(usbDevice)){
            HashMap<String, GPIOStatus> map=new HashMap<>();
            for (int i = 0; i < serialCount; i++) {
                for (int i1 = 0; i1 < GPIOCount; i1++) {
                    map.put(getKey(i,i1),new GPIOStatus());
                }
            }
            gpioMap.put(usbDevice,map);
        }else {
            LogUtil.d("the device already has flow record");
        }

    }

    private String getKey(int serialNumber,int GPIONumber){
        return String.format(Locale.getDefault(),"GPIO_%d_%d",serialNumber,GPIONumber);
    }


    private GPIOStatus queryGPIOStatus(UsbDevice usbDevice,int serialNumber,int GPIONumber)throws UartLibException {
        if(gpioMap.containsKey(usbDevice)){
            HashMap<String, GPIOStatus> map1 = gpioMap.get(usbDevice);
            String key = getKey(serialNumber, GPIONumber);
            if(map1==null || !map1.containsKey(key)){
                throw  new UartLibException("this device has no gpio record about the serial");
            }
            GPIOStatus gpioStatus = map1.get(key);
            LogUtil.d(String.format(Locale.getDefault(),"串口 %d获取GPIO %d状态：%s",serialNumber,GPIONumber,gpioStatus.toString()));
            return gpioStatus;
        }else {
            throw  new UartLibException("this device has no gpio record");
        }
    }

    private void setGPIOStatus(UsbDevice usbDevice,int serialNumber,int GPIONumber,@NonNull GPIOStatus newStatus)throws UartLibException {
        if(gpioMap.containsKey(usbDevice)){
            HashMap<String, GPIOStatus> map1 = gpioMap.get(usbDevice);
            String key = getKey(serialNumber, GPIONumber);
            if(map1==null || !map1.containsKey(key)){
                throw  new UartLibException("this device has no gpio record about the serial");
            }
            LogUtil.d(String.format(Locale.getDefault(),"串口 %d设置GPIO %d状态：%s",serialNumber,GPIONumber,newStatus.toString()));
            map1.put(key,newStatus);
            gpioMap.put(usbDevice,map1);
        }else {
            throw  new UartLibException("this device has no gpio record");
        }
    }

//    public boolean queryGPIOEnabled(UsbDevice usbDevice,int serialNumber,int GPIONumber) throws UartLibException {
//        GPIOStatus gpioStatus = queryGPIOStatus(usbDevice, serialNumber, GPIONumber);
//        assert(gpioStatus!=null);
//        return gpioStatus.isEnabled();
//    }
//
//    public void setGPIOEnabled(UsbDevice usbDevice,int serialNumber,int GPIONumber,boolean enabled) throws UartLibException {
//        GPIOStatus gpioStatus = queryGPIOStatus(usbDevice, serialNumber, GPIONumber);
//        assert(gpioStatus!=null);
//        gpioStatus.setEnabled(enabled);
//        setGPIOStatus(usbDevice,serialNumber,GPIONumber,gpioStatus);
//    }

    public GPIO_DIR queryGPIODir(UsbDevice usbDevice,int serialNumber,int GPIONumber) throws UartLibException {
        LogUtil.d("queryGPIODir: "+serialNumber+"-->"+GPIONumber);
        GPIOStatus gpioStatus = queryGPIOStatus(usbDevice, serialNumber, GPIONumber);
        assert(gpioStatus!=null);
        return gpioStatus.getDir();
    }

    public void setGPIODir(UsbDevice usbDevice,int serialNumber,int GPIONumber,GPIO_DIR dir) throws UartLibException {
        LogUtil.d("setGPIODir: "+GPIONumber);
        GPIOStatus gpioStatus = queryGPIOStatus(usbDevice, serialNumber, GPIONumber);
        assert(gpioStatus!=null);
        gpioStatus.setDir(dir);
        setGPIOStatus(usbDevice,serialNumber,GPIONumber,gpioStatus);
    }

    public GPIO_VALUE queryGPIOValue(UsbDevice usbDevice,int serialNumber,int GPIONumber) throws UartLibException {
        LogUtil.d("queryGPIOValue: "+GPIONumber);
        GPIOStatus gpioStatus = queryGPIOStatus(usbDevice, serialNumber, GPIONumber);
        assert(gpioStatus!=null);
        return gpioStatus.getValue();
    }

    public void setGPIOValue(UsbDevice usbDevice,int serialNumber,int GPIONumber,GPIO_VALUE value) throws UartLibException {
        LogUtil.d("setGPIOValue: "+GPIONumber);
        GPIOStatus gpioStatus = queryGPIOStatus(usbDevice, serialNumber, GPIONumber);
        assert(gpioStatus!=null);
        gpioStatus.setValue(value);
        setGPIOStatus(usbDevice,serialNumber,GPIONumber,gpioStatus);
    }

    public void restoreToDefault(UsbDevice usbDevice,int serialNumber,int GPIONumber) throws UartLibException {
        GPIOStatus gpioStatus = queryGPIOStatus(usbDevice, serialNumber, GPIONumber);
        assert(gpioStatus!=null);
        gpioStatus.setDir(GPIO_DIR.IN);
        gpioStatus.setValue(GPIO_VALUE.LOW);

        setGPIOStatus(usbDevice,serialNumber,GPIONumber,gpioStatus);
    }


    public void autoSetGPIOValLin(Short value){
        try {
            gpioLock.lock();
            gpiovalin=value;
        } finally {
            gpioLock.unlock();
        }

    }

    public Short getGPIOValLin(){
        try {
            gpioLock.lock();
            return gpiovalin;
        } finally {
            gpiovalin=null;
            gpioLock.unlock();
        }
    }

    public void clear(UsbDevice usbDevice){
        gpioMap.remove(usbDevice);
    }

    public static class GPIOStatus{


        private GPIO_VALUE value=GPIO_VALUE.LOW;
        private GPIO_DIR dir=GPIO_DIR.IN;

        public GPIOStatus() {

        }



        public GPIO_VALUE getValue() {
            return value;
        }

        public void setValue(GPIO_VALUE value) {
            this.value = value;
        }

        public GPIO_DIR getDir() {
            return dir;
        }

        public void setDir(GPIO_DIR dir) {
            this.dir = dir;
        }

        @Override
        public String toString() {
            return "GPIOStatus{" +
                    "value=" + value +
                    ", dir=" + dir +
                    '}';
        }
    }

}
